﻿using StockPredictor.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;
using log4net;
using StockPredictor.App_Start;
using System.Threading;

namespace NotificationManager
{
    class NotificationProcessingThread
    {
        private readonly ILog logger = LogManager.GetLogger(typeof(NotificationProcessingThread));

        /// <summary>
        /// Execution method for the Notification Processing Thread
        /// </summary>
        public void Run()
        {
            while (!Program.terminate)
            {
                // get all of the user preference entries from the database
                UserPreferencesRecordsContext context = new UserPreferencesRecordsContext();

                // for each entry, determine if we should send a notification
                foreach (UserPreferencesRecordModel model in context.UserPreferencesRecords.ToList())
                {
                    // get the username for this user
                    string userName = AccountUtilities.GetUserNameForUserId(model.UserId);

                    // determine if we should send a notification
                    if (ShouldSendNotification(model))
                    {
                        StringBuilder notificationBody = new StringBuilder();
                        notificationBody.AppendLine("StockPredictor Notification Results:");
                        notificationBody.AppendLine().AppendLine();

                        // determine if stocks to buy recommendations should be sent
                        if (model.NotificationSendStocksToBuy.Contains("Y"))
                        {
                            var stocksToBuy = StockValuation.GetStocksToBuy(userName);
                            notificationBody.AppendLine(GenerateRecommendationText(stocksToBuy, "Recommended Stocks to Buy"));
                        }

                        // determine if stocks to sell recommendations should be sent
                        if (model.NotificationSendStocksToSell.Contains("Y"))
                        {
                            var stocksToSell = StockValuation.GetStocksToSell(userName);
                            notificationBody.AppendLine(GenerateRecommendationText(stocksToSell, "Recommended Stocks to Sell"));
                        }

                        // send the notification email
                        string emailAddress = AccountUtilities.GetUser(model.UserId).EmailAddress;
                        if (emailAddress != "")
                        {
                            GenerateEmailNotification(emailAddress, notificationBody.ToString());
                        }

                        // update the last send time to current time
                        model.NotificationLastSentTime = DateTime.Now;
                        context.SaveChanges();
                    }
                }
                // go to sleep for 30 seconds
                Thread.Sleep(30000);
            }
        }

        /// <summary>
        /// Determine if notification should be sent
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        private bool ShouldSendNotification(UserPreferencesRecordModel model)
        {
            bool sendNotification = false;

            try
            {
                // calculate the next time we should send a notification
                DateTime nextSendTime = CalculateNextSendTime(model, model.NotificationLastSentTime);
                
                // see if we should send another notification
                if (DateTime.Now > nextSendTime)
                {
                    sendNotification = true;
                }
            }
            catch (Exception e)
            {
                logger.Error(e.ToString());
            }

            return sendNotification;
        }

        /// <summary>
        /// Calculates the next send time based upon the notification frequency
        /// </summary>
        /// <param name="model"></param>
        /// <param name="startingTime"></param>
        /// <returns></returns>
        private DateTime CalculateNextSendTime(UserPreferencesRecordModel model, DateTime startingTime)
        {
            DateTime nextSendTime;

            switch (model.NotificationFrequency)
            {
                // monthly
                case "M":
                    {
                        nextSendTime = startingTime.AddMonths(1);
                        break;
                    }
                // weekly
                case "W":
                    {
                        nextSendTime = startingTime.AddDays(7);
                        break;
                    }
                // daily
                case "D":
                    {
                        nextSendTime = startingTime.AddDays(1);
                        break;
                    }
                // testing 
                case "TEST":
                    {
                        nextSendTime = startingTime.AddMinutes(5);
                        break;
                    }
                default:
                    {
                        throw new Exception("Invalid Notification Frequency Specified of: " + model.NotificationFrequency);
                    }
            }

            return nextSendTime;
        }

        /// <summary>
        /// Generates the formatted recommendation text
        /// </summary>
        /// <param name="recommendedStocks"></param>
        /// <param name="recommendationTitle"></param>
        /// <returns></returns>
        private string GenerateRecommendationText(List<CurrentStock> recommendedStocks, string recommendationTitle)
        {
            StringBuilder body = new StringBuilder();
            body.AppendLine(recommendationTitle);
            body.AppendLine("--------------------------------");
            foreach (CurrentStock stock in recommendedStocks)
            {
                body.AppendLine("Name: " + stock.Name + " Symbol: " + stock.Symbol + " Recent Change: " + stock.Change);
            }
            
            // append an empty line
            body.AppendLine();

            return body.ToString();
        }

        /// <summary>
        /// Generates the email notification
        /// </summary>
        /// <param name="destinationEmailAddress"></param>
        private void GenerateEmailNotification(string destinationEmailAddress, string body)
        {
            var fromAddress = new MailAddress("stockpredictor.notify@gmail.com", "StockPredictor Notification Service");
            var toAddress = new MailAddress(destinationEmailAddress, "");
            const string fromPassword = "U4E6Q9Az";
            const string subject = "Stock Predictor Recommendations";

            var smtp = new SmtpClient
            {
                Host = "smtp.gmail.com",
                Port = 587,
                EnableSsl = true,
                DeliveryMethod = SmtpDeliveryMethod.Network,
                UseDefaultCredentials = false,
                Credentials = new NetworkCredential(fromAddress.Address, fromPassword)
            };
            using (var message = new MailMessage(fromAddress, toAddress)
            {
                Subject = subject,
                Body = body
            })
            {
                smtp.Send(message);
            }
        }
    }
}
